import random

import libtcodpy as libtcod

import data
import backend
import draw


def get_input(scene, player, console):
    output = []
    fov_redraw = False
    if player.WAIT <= 0:
        if player.destination:
            return automated_travel(scene, player)

        if player.stats['stamina'] > 0:
            key, key_pressed, mouse = player_input()
            fov_redraw, output, move_type = handle_player_input(key, key_pressed, mouse, scene, player, console)

        else:
            backend.move(player, (0,0))
            output = ('Waited to regain stamina.',)
    return fov_redraw, output

def player_input():
    """
    gets input from the mouse or keyboard and formats it nicely to pass along
    """
    key_pressed = libtcod.console_check_for_keypress(libtcod.KEY_PRESSED)
    mouse = libtcod.mouse_get_status()
    if key_pressed.vk == libtcod.KEY_CHAR:
        key = chr(key_pressed.c)
    else:
        key = key_pressed.vk
    return key, key_pressed, mouse

def handle_player_input(key, key_pressed, mouse, scene, player, console):
    """
    handles gotten player input and does all its supposed to do hopefully!
    """
    fov_redraw = False
    output = []
    move_type = 'normal'
    if key_pressed.lctrl:
        move_type = 'sneak'
    if key_pressed.lalt:
        move_type = 'run'
    if key in data.move_keys:
        if data.LINE_TRAVEL:
            next_tile = player.location + data.move_keys[key]
            path = []
            while next_tile in scene.floors and not next_tile in scene.doors:
                path.append(next_tile)
                next_tile += data.move_keys[key]
            if path:
                player.path = path
                player.destination = path[len(path)-1]
            data.LINE_TRAVEL = False

        else:
            m = backend.move(player, data.move_keys[key], move_type=move_type)
            if m == 'moved':
                fov_redraw = True
            elif m == 'cancelled':
                output.append('Can\'t move there')
            else:
                output.append(('attack', m))
    if mouse.lbutton_pressed and libtcod.map_is_in_fov(scene.fov_map, mouse.cx, mouse.cy):
        for unit in scene.units:
            if unit.location == (mouse.cx, mouse.cy):
                player.target = unit
    if key == 'a':
        fov_redraw, output = scene.spawn()
    if mouse.rbutton_pressed and libtcod.map_is_in_fov(scene.fov_map, mouse.cx, mouse.cy):
        stats = {'strength': random.randint(1, 5), 'stamina': random.randint(1, 5), 'focus': random.randint(1, 5)}
        fov_redraw, output = scene.spawn(unit=backend.Unit(character='U', stats=stats), location=(mouse.cx, mouse.cy), pushing=True)
        #fov_redraw, output = scene.spawn('Unit', location=(mouse.cx, mouse.cy), pushing=True)
    if key == ',':
        result = pickup(player, scene, console)
        if result:
            output.append(result)
    if key == 'w':
        data.LINE_TRAVEL = True
    if key == 'e':
        choice = draw.menu(console, 'Equip what?', [['{} {}'.format(item.quantity, item), item] for item in player.inventory], 20)
        if choice is not None:
            player.equip(choice)
            output.append('Equipped the {}.'.format(choice.name))
    if key == 'E':
        choice = draw.menu(console, 'Unequip what?', [['{}: {}'.format(slot, player.equipment[slot]), slot] for slot in data.EQUIPMENT_SLOTS], 30)
        if choice is not None:
            item = player.equipment[choice]
            if item:
                player.unequip(slot=choice)
                output.append('Unequipped the {}.'.format(item.name))      
    if key == 'd':
        choice = draw.menu(console, 'Drop what?', [['{} {}'.format(item.quantity, item), item] for item in player.inventory], 20)
        if choice is not None:
            backend.drop(player, choice)
            output.append('Dropped the {}.'.format(choice.name))
    if key == 'm':
        scene.forget_map()
        scene.grid, scene.rooms, scene.doors = scene.make_map(data.MIN_LEAF_SIZE, data.MIN_ROOM_SIZE)
        scene.set_fov()
        for unit in scene.units:
            unit.location = scene.random_floor_tile()
        scene.items = set()
        scene.danger_level += 1            
        scene.spawn()
        scene.spawn()
        scene.spawn()
        scene.spawn()
        scene.spawn()
        scene.spawn()
        fov_redraw = True
        output.append('Now on level {}.'.format(scene.danger_level))

    if key == 's':
        output = skill_menu(player, console)
                
    if key == 'o':
        result = auto_explore(player, scene, auto_pickup=data.AUTO_PICKUP)
        if result:
            output.append(result)

    if key == 'i':
        targets = sorted([unit for unit in scene.units if libtcod.map_is_in_fov(scene.fov_map, unit.location[0], unit.location[1]) and not unit.is_player],
               key=lambda u: u.location.distance(player.location))
        if targets and not player.target:
            player.target = targets[0]
        if player.target:
            m = backend.move(player, backend.a_star(player.location, player.target.location, scene, unit_check=False)[0] - player.location, move_type=move_type)
            if m == 'moved':
                fov_redraw = True
            else:
                output.append(('attack', m))
    if key == libtcod.KEY_TAB:
        targets = sorted([unit for unit in scene.units if libtcod.map_is_in_fov(scene.fov_map, unit.location[0], unit.location[1]) and not unit.is_player],
               key=lambda u: u.location.distance(player.location))
        if targets and not player.target:
            player.target = targets[0]
        elif player.target in targets:
            player.target = targets[(targets.index(player.target) + 1) % len(targets)]
        if player.target:
            output.append('Targeting {} at {}.'.format(player.target.name, player.target.location))
   
    return fov_redraw, output, move_type

def auto_pickup(player, scene):
    """
    sort the items in view by proximity, path to the nearest
    """
    items = sorted([item for item in scene.items if libtcod.map_is_in_fov(scene.fov_map, item.location[0], item.location[1])], 
                    key = lambda i:i.location.distance(player.location))
    if items:
        player.destination = items[0].location
        player.path = backend.a_star(player.location, items[0].location, scene, False)
        player.path_index = 0
    else:
        return 'No items in sight.'

def pickup(player, scene, console):
    """
    attempt to pickup any items at the player's feet
    """
    items = [[item.name, item] for item in scene.items if item.location == player.location]
    if items:
        if len(items) > 1:
            choice = draw.menu(console, 'Pickup what?', items, 20)
        else:
            choice = items[0][1]
        if choice is not None:
            backend.pickup(player, choice, console)
            return 'Picked up the {}.'.format(choice.name)
    elif data.AUTO_PICKUP:
        result = auto_pickup(player, scene)
        if result:
            return result 
def automated_travel(scene, player):
    """
    a place holder for the player's Brain's turn() method. traverses along player.path until he gets there or spots an enemy.

    args:
        player: the player to move
        scene: the scene to move through

    returns:
        fov_redraw, data to be output
    """
    spotted = [unit for unit in scene.units if not unit.is_player and libtcod.map_is_in_fov(scene.fov_map, unit.location[0], unit.location[1])]
    if not spotted:
        #if data.AUTO_PICKUP:
        #    pickup(player, scene) #sets new destination and path towards an item
        tloc = player.path[player.path_index]
        backend.move(player, tloc - player.location)
        if player.path[player.path_index] == player.destination:
            player.destination = None
            player.path_index = 0
        else:
            player.path_index += 1
        return True, []
    else:
        player.destination = None
        player.path_index = 0
        return False, ('Too dangerous to travel.',)

def auto_explore(player, scene, auto_pickup=False):
    """
    a rather boringly intelligent autoexplore algorithm. will take you to the nearest (as the bird flies) room
    
    args:
        player: the player to move
        scene: the scene to move through

    returns:
        data to be output
    """
    nearby_rooms = sorted(player.scene.rooms, key=lambda r: r.center.distance(player.location))
    for room in nearby_rooms:
        for tile in room.corners:
            if not scene.explored[tile[0]][tile[1]]:
                player.destination = tile
                player.path = backend.a_star(player.location, tile, scene, unit_check=False)
                player.path_index = 0
                break
        if player.destination:
            break
    if player.destination:
        output = 'Beginning autoexplore...'
    else:
        output = 'Done exploring.'
    return output

def skill_menu(player, console):
    output = []
    choice = draw.menu(console, 'Improve what skill?', [['{:9}: {}'.format(skill.capitalize(), data.SKILL_DESC[skill]), skill] 
                                                      for skill in data.SKILL_LIST], 70)
    if choice is not None:
        s_cost = data.SKILL_COST*(player.skill_ranks[choice]+1)
        a_cost = (len([ability for ability in player.abilities if ability in [a[0] for a in data.ABILITIES[skill]]]) + 1)*data.ABILITY_COST
        options = [(choice.capitalize(), choice, 'Gain a rank of {}'.format(choice), '({} exp)'.format(s_cost))]
        for a in data.ABILITIES[choice]:
            if a in player.abilities:
                options.append(['{}: {} (gained)', a])
            else:
                options.append((a[0], a, a[1], '({} {}, {} exp)'.format(a[2], choice, a_cost)))
        second_choice = draw.menu(console, '{} training'.format(choice.capitalize()), options, 70, shop=True, confirm_msg='Right/Enter')

        if second_choice is not None:
            if second_choice in player.skills:
                if player.exp >= s_cost:
                    player.exp -= s_cost
                    player.skill_up(second_choice)
                    output.append('Trained {}'.format(second_choice))
                else:
                    output.append('Insufficient exp.')
            else:
                if player.exp < a_cost:
                    output.append('Insufficient exp.')
                if player.skills[choice] < second_choice[2]:
                    output.append('Insufficient {}'.format(choice))
                elif player.exp >= a_cost:
                    player.gain_ability(second_choice[0])
                    player.exp -= a_cost
                    output.append('Trained {}.'.format(second_choice[0]))

        else:
            skill_menu(player, console)
    return output